// check pointer
owl_bool owl_check_str_pointer(owl_byte * src, owl_umax size)
{
  return (src != NULL && size >= sizeof(owl_str));
}

// check members
owl_bool owl_check_str_members(owl_byte * ptr, owl_umax size, owl_wchar_enc enc)
{
  // check params
  if (owl_check_ptr(ptr, size) == owl_false
      || owl_check_wchar_enc(enc) == owl_false)
    goto err;
  
  // set the encoding function
  owl_byte (* check_chenc)(owl_byte *, owl_umax) = OWL_STR_FUNCS[enc].check_wchar_enc;  
  // check string encoding
  for (owl_umax i = 0; i < size; /**/) {
    owl_byte tmp = check_chenc(ptr + i, size - i);
    if (tmp == 0) goto err;
    i += tmp;
  }
  
  // end
  return owl_true;
  err:
  return owl_false;
}

// check all
owl_bool owl_check_str_all(owl_str * data, owl_umax size)
{
  return owl_check_str_pointer((void *) data, size) 
         && owl_check_str_members(data -> arr -> ptr, data -> arr -> size, data -> enc);
}

// create
owl_str * owl_create_str(owl_byte * data, owl_umax size, owl_wchar_enc enc)
{
  // check params
  owl_str * str = NULL;
  owl_mem_space * mem_space = NULL;
  if (owl_check_str_members(data, size, enc) == owl_false)
    goto err;
    
  // create the structure 
  // it will need 2 allocations:
  // - one for the structure itself
  // - the other one for the mem_space inside the struct
  str = (void *) (owl_create_mem_space(sizeof(owl_str)) -> ptr);
  if (str == NULL)
    goto err;
  // setup the encoding functions
  *str = OWL_STR_FUNCS[enc];
  // assign the mem_space struct
  mem_space = (owl_mem_space *) owl_create_bytarr(data, size);
  if (mem_space == NULL) goto err;
  str -> arr = mem_space;
  // check if the encoded string is valid
  for (owl_umax i = 0; i < str -> arr -> size; /**/) {
    owl_byte tmp = str -> check_wchar_enc(str -> arr -> ptr + i, str -> arr -> size - i);
    if (tmp == 0) goto err;
    i += tmp;
  }
  
  // end
  return str;
  err:
  owl_free_mem_space(&((owl_mem_space) {(void *) str, sizeof(owl_str)}));
  owl_free_mem_space(mem_space);
  return NULL;
}

// free
owl_umax owl_free_str(owl_str * str)
{
  // check params
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false)
    goto err;
  // free the memory allocated by the struct
  owl_umax tmp = owl_free_mem_space(str -> arr);
  if (tmp == 0) goto err;
  tmp = owl_free_mem_space(&((owl_mem_space) {(void *) str, sizeof(owl_str)}));
  if (tmp == 0) goto err;  
  // return the number of free slots on the mem_space table
  return tmp;
  err:
  return 0;
}

// print
owl_umax owl_print_str(owl_byte * src, owl_umax size)
{
  // check params
  printf("OWL_STR struct info:\n");
  printf("  Struct info: %p, %llu\n", src, size);
  if (owl_check_str_all((void *) src, sizeof(owl_str)) == owl_false)
    return 0;
  
  // else print the structure
  owl_str * str = (void *) src;
  printf("  String pointer: %p\n", str -> arr -> ptr);
  printf("  String size (bytes): %llu\n", str -> arr -> size);
  printf("  String bytes (hex): ");
  for (owl_umax i = 0; i < (str -> arr -> size); i++)
    printf("%02X ", (str -> arr -> ptr)[i]);
  putchar('\n');
  printf("  String encoding: %s\n", OWL_WCHAR_ENC_STR[str -> enc]);
  printf("  String characters (hex): ");
  for (owl_umax i = 0; i < (str -> arr -> size); putchar(' '))
    i += str -> print_wchar_enc(str -> arr -> ptr + i, str -> arr -> size - i);
  putchar('\n');
  return sizeof(owl_str);
}

// compare
owl_char owl_comp_str(owl_byte * str1, owl_byte * str2)
{
  // check params
  owl_str * str1_tmp = (void *) str1,
          * str2_tmp = (void *) str2;
  // check params
  if (owl_check_str_all(str1_tmp, sizeof(owl_str)) == owl_false
      || owl_check_str_all(str2_tmp, sizeof(owl_str)) == owl_false
      || str1_tmp -> enc != str2_tmp -> enc)
      return owl_comp_err;
  // the only way 2 strings can be equal will be if the encoding and characters match in each position
  // the great and less than comparisons will happen when comparing the first non-matching character
  // integers in the string or with the string byte size directly
  if (str1_tmp -> arr -> size > str2_tmp -> arr -> size)
    return owl_comp_greater;
  else if (str1_tmp -> arr -> size < str2_tmp -> arr -> size)
    return owl_comp_less;
  // they have equal byte sizes
  for (owl_umax i = 0; i < (str1_tmp -> arr -> size); /**/) {
    owl_umax tmp = i,
             ch1 = str1_tmp -> get_wchar(str1_tmp -> arr -> ptr + i, str1_tmp -> arr -> size - i),
             ch2 = str2_tmp -> get_wchar(str2_tmp -> arr -> ptr + i, str2_tmp -> arr -> size - i);
    i += str1_tmp -> check_wchar(ch1);
    if (tmp == i || str2_tmp -> check_wchar(ch2) == 0)
      return owl_comp_err;
    if (ch1 > ch2)
      return owl_comp_greater;
    if (ch1 < ch2)
      return owl_comp_less;
  }
  // legit real equal strings
  return owl_comp_equal;
}

// gets the integer value associated with the
// index of a character in str -> num
owl_byte owl_get_num_ch_as_num(owl_str * str, owl_wchar ch)
{
  // check params
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false
      || str -> check_wchar(ch) == 0)
    return 0;
  
  // return the value
  if (ch == (str -> num)[OWL_WCHAR_NUM_1]) return 1;
  else if (ch == (str -> num)[OWL_WCHAR_NUM_2]) return 2;
  else if (ch == (str -> num)[OWL_WCHAR_NUM_3]) return 3;
  else if (ch == (str -> num)[OWL_WCHAR_NUM_4]) return 4;
  else if (ch == (str -> num)[OWL_WCHAR_NUM_5]) return 5;
  else if (ch == (str -> num)[OWL_WCHAR_NUM_6]) return 6;
  else if (ch == (str -> num)[OWL_WCHAR_NUM_7]) return 7;
  else if (ch == (str -> num)[OWL_WCHAR_NUM_8]) return 8;
  else if (ch == (str -> num)[OWL_WCHAR_NUM_9]) return 9;
  else if (ch == (str -> num)[OWL_WCHAR_NUM_A_UP] || ch == (str -> num)[OWL_WCHAR_NUM_A_LOW]) return 10;
  else if (ch == (str -> num)[OWL_WCHAR_NUM_B_UP] || ch == (str -> num)[OWL_WCHAR_NUM_B_LOW]) return 11;
  else if (ch == (str -> num)[OWL_WCHAR_NUM_C_UP] || ch == (str -> num)[OWL_WCHAR_NUM_C_LOW]) return 12;
  else if (ch == (str -> num)[OWL_WCHAR_NUM_D_UP] || ch == (str -> num)[OWL_WCHAR_NUM_D_LOW]) return 13;
  else if (ch == (str -> num)[OWL_WCHAR_NUM_E_UP] || ch == (str -> num)[OWL_WCHAR_NUM_E_LOW]) return 14;
  else if (ch == (str -> num)[OWL_WCHAR_NUM_F_UP] || ch == (str -> num)[OWL_WCHAR_NUM_F_LOW]) return 15;
  return 0;
}

// gets the index of a character in str -> num
// with the integer value associated with it
owl_byte owl_get_num_as_num_ch_pos(owl_byte num)
{
  // return the index
  if (num == 1) return OWL_WCHAR_NUM_1;
  if (num == 2) return OWL_WCHAR_NUM_2;
  if (num == 3) return OWL_WCHAR_NUM_3;
  if (num == 4) return OWL_WCHAR_NUM_4;
  if (num == 5) return OWL_WCHAR_NUM_5;
  if (num == 6) return OWL_WCHAR_NUM_6;
  if (num == 7) return OWL_WCHAR_NUM_7;
  if (num == 8) return OWL_WCHAR_NUM_8;
  if (num == 9) return OWL_WCHAR_NUM_9;
  if (num == 10) return OWL_WCHAR_NUM_A_UP;
  if (num == 11) return OWL_WCHAR_NUM_B_UP;
  if (num == 12) return OWL_WCHAR_NUM_C_UP;
  if (num == 13) return OWL_WCHAR_NUM_D_UP;
  if (num == 14) return OWL_WCHAR_NUM_E_UP;
  if (num == 15) return OWL_WCHAR_NUM_F_UP;
  return OWL_WCHAR_NUM_0;
}

// gets a pointer to the character integer
// in str -> num associated with an integer
owl_wchar * owl_get_num_as_num_ch(owl_str * str, owl_byte num)
{
  // check params
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false)
    return NULL;
  
  // return the number character integer
  if (num == 0) return str -> num + OWL_WCHAR_NUM_0;
  else if (num == 1) return str -> num + OWL_WCHAR_NUM_1;
  else if (num == 2) return str -> num + OWL_WCHAR_NUM_2;
  else if (num == 3) return str -> num + OWL_WCHAR_NUM_3;
  else if (num == 4) return str -> num + OWL_WCHAR_NUM_4;
  else if (num == 5) return str -> num + OWL_WCHAR_NUM_5;
  else if (num == 6) return str -> num + OWL_WCHAR_NUM_6;
  else if (num == 7) return str -> num + OWL_WCHAR_NUM_7;
  else if (num == 8) return str -> num + OWL_WCHAR_NUM_8;
  else if (num == 9) return str -> num + OWL_WCHAR_NUM_9;
  else if (num == 10) return str -> num + OWL_WCHAR_NUM_A_UP;
  else if (num == 11) return str -> num + OWL_WCHAR_NUM_B_UP;
  else if (num == 12) return str -> num + OWL_WCHAR_NUM_C_UP;
  else if (num == 13) return str -> num + OWL_WCHAR_NUM_D_UP;
  else if (num == 14) return str -> num + OWL_WCHAR_NUM_E_UP;
  else if (num == 15) return str -> num + OWL_WCHAR_NUM_F_UP;
  return NULL;
}

// gets the number of characters in a string
owl_umax owl_get_str_ch_length(owl_str * str)
{
  // check parameters
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false)
    return 0;
  owl_umax ch_count = 0;
  for (owl_umax i = 0;
       i < str -> arr -> size;
       i += str -> check_wchar_enc(str -> arr -> ptr + i, str -> arr -> size - i))
    ch_count++;
  return ch_count;
}

// changes the encoding of a string
// it can be possible to make the change or not depending on the characters
owl_bool owl_str_transcode(owl_str * str, owl_wchar_enc enc)
{
  // check params
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false)
    return owl_false;
  // why
  if (enc == str -> enc)
    return owl_true;
  
  // dest encoding functions
  owl_str dest = OWL_STR_FUNCS[enc];  
  // check if each character can be converted to the new encoding
  // convert to unicode -> convert from unicode to the encoding
  owl_umax ch_length = owl_get_str_ch_length(str),
           dest_enc_length = 0;
  
  // create a owl_wchar array for the characters when being converted to unicode
  owl_mem_space * unicode = owl_create_mem_space(ch_length * sizeof(owl_wchar));
  if (unicode == NULL)
    return owl_false;
  
  // check if the conversion can be done (while storing the unicode converted characters)
  for (owl_umax i = 0, j = 0; i < str -> arr -> size; j++)
  {
    // get the unicode translation
    owl_wchar tmp = 0, * ptr = ((owl_wchar *) (unicode -> ptr)) + j;
    *ptr = str -> wchar_as_unicode(str -> get_wchar(str -> arr -> ptr + i, str -> arr -> size - i));
    // check if the current unicode character is valid for conversion
    if ((tmp = dest.wchar_unicode_as(*ptr)) == dest.inv) {
      owl_free_mem_space(unicode);
      return owl_false;
    }
    // increase i for the next loop
    i += str -> check_wchar_enc(str -> arr -> ptr + i, str -> arr -> size - i);
    // get the size of the new encoded string with each conversion
    dest_enc_length += dest.check_wchar(tmp);
  }
  
  // all good for conversion
  // create the new encoded string
  owl_mem_space * new = owl_create_mem_space(dest_enc_length);
  if (new == NULL) {
    owl_free_mem_space(unicode);
    return owl_false;
  }
  
  // go through unicode -> ptr converting the characters into new
  owl_byte * ptr1 = new -> ptr;
  for (owl_umax j = 0; ptr1 < new -> ptr + new -> size; j++) {
    owl_wchar * ptr2 = ((owl_wchar *) (unicode -> ptr)) + j;
    if ((ptr1 = dest.write_wchar_enc(dest.wchar_unicode_as(* ptr2), ptr1, new -> size)) == NULL) {
      // ^ already checked the size and characters so it isn't necessary
      // to put the exact remainder size of new in the function's argument
      owl_free_mem_space(unicode);
      owl_free_mem_space(new);
      return owl_false;
    }
  }
  
  // assign the new encoding functions
  *str = dest;
  // assign the new mem_space to str
  str -> arr = new;
  // free the memory used
  owl_free_mem_space(unicode);
  return owl_true;
}

// returns the wchar
owl_byte * owl_str_ch_at_index(owl_str * str, owl_umax index)
{
  // check params
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false)
    goto err;
  // check if index is a possible value
  owl_umax ch_length = owl_get_str_ch_length(str);
  if (index >= ch_length)
    goto err;
  // find the character
  for (owl_umax i = 0, j = 0; i < str -> arr -> size;
       i += str -> check_wchar_enc(str -> arr -> ptr + i, str -> arr -> size - i), j++)
    if (j == index) // return the character position
      return str -> arr -> ptr + i;
  // second error
  err:
  return NULL;
}

// returns an owl_wchar[] with the integer values encoded in a string (remember to free te memory allocated)
owl_mem_space * owl_decode_str(owl_str * str)
{
  // check params
  owl_mem_space * rtn = NULL;
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false)
    goto err;
  
  // build the owl_wchar array
  owl_umax ch_length = owl_get_str_ch_length(str);
  rtn = owl_create_mem_space(ch_length * sizeof(owl_wchar));
  if (rtn == NULL)
    goto err;
  for (owl_umax i = 0; i < ch_length; i++)
    ((owl_wchar *) rtn -> ptr)[i] = str -> get_wchar(owl_str_ch_at_index(str, i), str -> arr -> size);
  
  // end
  return rtn;
  err:
  owl_free_mem_space(rtn);
  return NULL;
}

// returns a str structure from an owl_wchar array 
// (size is the number of elements to read of size owl_wchar)
owl_str * owl_encode_wchars(owl_wchar * arr, owl_umax size, owl_wchar_enc enc)
{
  // check params
  if (owl_check_ptr(arr, size) == owl_false
      || owl_check_wchar_enc(enc) == owl_false)
    return NULL;
  
  // get the encoding functions
  owl_str enc_funcs = OWL_STR_FUNCS[enc];
  // get the size of the encoded string
  owl_umax str_size = 0;
  for (owl_umax i = 0; i < size; i++) {
    owl_umax ch_size = enc_funcs.check_wchar(arr[i]);
    if (ch_size == 0)
      return NULL;
    str_size += ch_size;
  }
  // create a mem_space struct to hold the encoded string
  owl_mem_space * tmp = owl_create_mem_space(str_size);
  if (tmp == NULL)
    return NULL;
  owl_byte * ptr = tmp -> ptr;
  for (owl_umax i = 0; i < size; i++)
    ptr = enc_funcs.write_wchar_enc(arr[i], ptr, str_size); 
    // ^ already calculated the string size so no problems in using str_size as argument
  
  // create the string structure
  owl_str * str = owl_create_str(tmp -> ptr, tmp -> size, enc);
  owl_free_mem_space(tmp);
  return str;
}

// reverse the order of the characters in a string
owl_bool owl_reverse_str(owl_str * str)
{
  // check params
  owl_mem_space * dec = NULL;
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false)
    goto err;
  
  // get the decoded version of the string, reverse the 
  // associated array and encode that array back to a string
  dec = owl_decode_str(str);
  if (dec == NULL)
    goto err;
    
  // reverse the owl_wchar array
  owl_arr tmp1 = {dec, {OWL_TYPE_WCHAR, sizeof(owl_wchar), owl_print_wchar, owl_comp_wchar}};
  if (owl_reverse_arr(&tmp1) == 0)
    goto err;
  // encode the characters
  owl_str * tmp2 = owl_encode_wchars((owl_wchar *) dec -> ptr, dec -> size / sizeof(owl_wchar), str -> enc);
  if (tmp2 == NULL)
    goto err;
  
  // write the reversed string byte per byte on str -> arr -> ptr
  owl_free_mem_space(dec);
  owl_copy_bytarr((void *) tmp2 -> arr, (void *) str -> arr);
  owl_free_str(tmp2);
  return owl_true;
  err: // error
  owl_free_mem_space(dec);
  return owl_false;
}

// match find string inside str string
owl_byte * owl_find_str_chs(owl_str * str, owl_str * find)
{
  // check params
  // check params
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false
      || owl_check_str_all(find, sizeof(owl_str)) == owl_false)
    return NULL;
  
  // reuse owl_search_bytarr()
  owl_bytarr tmp1 = {str -> arr -> ptr, str -> arr -> size},
             tmp2 = {find -> arr -> ptr, find -> arr -> size};
  return owl_search_bytarr(&tmp1, &tmp2).ptr;
}

// check if a byte position on the string is not in the middle of a character's data
owl_bool owl_check_str_pos(owl_str * str, owl_umax pos)
{
  // check params
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false
      || pos > str -> arr -> size)
    return owl_false;
  // locate the position on the string
  for (owl_umax i = 0; i < str -> arr -> size; /**/) {
    // check if pos corresponds to the start of a character
    if (i == pos)
      break;
    else if (i > pos)
      return owl_false;
    i += str -> check_wchar_enc(str -> arr -> ptr + i, str -> arr -> size - i);
  }
  return owl_true;
}

// insert characters
owl_bool owl_insert_str_chs(owl_str * str, owl_umax pos, owl_str * insert)
{
  // check params
  owl_mem_space * new = NULL;
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false
      || owl_check_str_all(insert, sizeof(owl_str)) == owl_false
      || owl_check_str_pos(str, pos) == owl_false
      || pos > str -> arr -> size)
    goto err;
  
  // create a new mem_space struct and copy all the characters there
  new = owl_create_mem_space(str -> arr -> size + insert -> arr -> size);
  if (new == NULL)
    goto err;
  
  // temporal bytarr variables
  owl_bytarr tmp1 = {str -> arr -> ptr, pos}, tmp2 = {new -> ptr, new -> size};
  
  // copy the first section of str (if it exists)
  if (pos != 0)
    tmp2 = owl_copy_bytarr(&tmp1, (owl_bytarr *) new);
  // copy insert
  tmp1.ptr = insert -> arr -> ptr;
  tmp1.size = insert -> arr -> size;
  tmp2 = owl_copy_bytarr(&tmp1, &tmp2);
  // copy the rest of str (if it exists)
  if (pos != str -> arr -> size - 1) {
    tmp1.ptr = str -> arr -> ptr + pos;
    tmp1.size = str -> arr -> size - pos;
    tmp2 = owl_copy_bytarr(&tmp1, &tmp2);
  }
  
  // replace the str -> arr structure with new
  owl_free_mem_space(str -> arr);
  str -> arr = new;
  return owl_true;
  err:
  owl_free_mem_space(new);
  return owl_false;
}

// delete characters
owl_bool owl_delete_str_chs(owl_str * str, owl_umax pos, owl_umax rm_size)
{
  // check params
  owl_mem_space * new = NULL;
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false
      || pos + rm_size > str -> arr -> size
      || rm_size == 0
      || owl_check_str_pos(str, pos) == owl_false
      || rm_size == str -> arr -> size) // delete the string instead
    goto err;
      
  // also check if the erasing does not leave character data leftovers
  if (owl_check_str_members(str -> arr -> ptr + pos, rm_size, str -> enc) == owl_false)
    goto err;
  
  // transfer the wanted data to another mem_space struct
  new = owl_create_mem_space(str -> arr -> size - rm_size);
  if (new == NULL)
    goto err;
    
  // temporal bytarr variables
  owl_bytarr tmp1 = {str -> arr -> ptr, pos}, tmp2 = {new -> ptr, new -> size};  
  // copy the first section of str (if it exists)
  if (pos != 0)
    tmp2 = owl_copy_bytarr(&tmp1, (owl_bytarr *) new);
  // ignore the rm section
  // copy the rest of str (if it exists)
  if (pos != str -> arr -> size - rm_size - 1) {
    tmp1.ptr = str -> arr -> ptr + pos + rm_size;
    tmp1.size = str -> arr -> size - pos - rm_size;
    tmp2 = owl_copy_bytarr(&tmp1, &tmp2);
  }
  
  // replace the str -> arr structure with new
  owl_free_mem_space(str -> arr);
  str -> arr = new;
  return owl_true;
  err:
  owl_free_mem_space(new);
  return owl_false;
}

// skip characters in a string
// - skip until no characters in skip (search_until == owl_false)
// - skip until a character in skip (search_until == owl_true)
owl_byte * owl_skip_str_chs(owl_str * str, owl_bool search_until, owl_str * skip)
{
  // check params
  owl_mem_space * skip_ints = NULL;
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false
      || owl_check_str_all(skip, sizeof(owl_str)) == owl_false
      || str -> enc != skip -> enc)
    goto err;
  
  // check if the current character in str to be read isn't contained in skip
  skip_ints = owl_decode_str(skip);
  if (skip_ints == NULL)
    goto err;  
  owl_wchar * ints = (void *) skip_ints -> ptr;
  for (owl_umax i = 0; i < str -> arr -> size; /**/) {
    owl_wchar cur_ch = str -> get_wchar(str -> arr -> ptr + i, str -> arr -> size - i);
    owl_bool ch_found = owl_false;
    for (owl_umax j = 0; j < skip_ints -> size / sizeof(owl_wchar); j++)
      ch_found = ch_found || (cur_ch == ints[j]);
    if (ch_found == search_until)
      return str -> arr -> ptr + i;
    i += str -> check_wchar_enc(str -> arr -> ptr + i, str -> arr -> size - i);
  }
  
  // reached end of string
  owl_free_mem_space(skip_ints);
  return str -> arr -> ptr + str -> arr -> size;
  // error
  err:
  owl_free_mem_space(skip_ints);
  return NULL;
}

// join strings function
owl_bool owl_join_strs(owl_str * str1, owl_str * str2)
{
  // check params
  if (owl_check_str_all(str1, sizeof(owl_str)) == owl_false
      || owl_check_str_all(str2, sizeof(owl_str)) == owl_false)
    return owl_false;
  
  // reuse owl_insert_str_chs()
  if (owl_insert_str_chs(str1, str1 -> arr -> size, str2) == owl_false)
    return owl_false;
  return owl_true;
}

// find a single character by its integer value
owl_byte * owl_find_str_ch(owl_str * str, owl_wchar ch)
{
  // check params
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false
      || str -> check_wchar(ch) == 0)
    return NULL;
  
  // find said character
  for (owl_umax i = 0; i < str -> arr -> size; /**/) {
    owl_wchar tmp = str -> get_wchar(str -> arr -> ptr + i, str -> arr -> size);
    if (tmp == ch) return str -> arr -> ptr + i;
    i += str -> check_wchar(tmp);
  }
  
  // no character was found
  return NULL;
}

// get a string up to a certain character
owl_str * owl_get_str_until_ch(owl_byte * data, owl_umax size, owl_wchar_enc enc, 
                               owl_wchar ch, owl_bool keep_last_ch)
{
  // check params
  if (owl_check_wchar_enc(enc) == owl_false) 
    goto err;
  owl_mem_space sp = {data, size};
  owl_str str = OWL_STR_FUNCS[enc];
  str.arr = &sp;
  if (owl_check_str_all(&str, sizeof(owl_str)) == owl_false)
    goto err;
  
  // search for the character and calculate the size
  owl_umax i = 0;
  for (/**/; i < size; /**/) {
    owl_byte ch_size = str.check_wchar_enc(data + i, size);
    if (ch_size == 0) goto err;
    owl_wchar ch_tmp = str.get_wchar(data + i, size);
    if (ch_tmp == ch)
      return owl_create_str(data, i + (keep_last_ch == owl_false ? 0 : ch_size), enc);
    i += ch_size;
  }
  
  // no character was found
  err:
  return NULL;
}

// check integer inside a string as an octal number
owl_umax owl_check_str_as_oct(owl_str * str, owl_byte * ptr)
{
  // check params
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false
      || ptr < (str -> arr -> ptr)
      || ptr > (str -> arr -> ptr) + (str -> arr -> size) - 1
      || owl_check_str_pos(str, ptr - str -> arr -> ptr) == owl_false)
    return 0;
  
  // first character must be 0/ptr must point to the start of a character's data
  owl_umax size = (str -> arr -> ptr) + (str -> arr -> size) - ptr,
           i = str -> check_wchar_enc(ptr, size);
  if (str -> get_wchar(ptr, size) != (str -> num)[OWL_WCHAR_NUM_0])
    return 0;
  if (size - i == 0) // special case: it is the number 0
    return i;
  
  // check each character to see if it is from an octal integer
  // return the size in bytes of the number read
  for (/**/; i < size; /**/) {
    owl_wchar cur_ch = str -> get_wchar(ptr + i, size - i);
    owl_bool ch_found = owl_false;
    for (owl_umax j = OWL_WCHAR_NUM_0; j < OWL_WCHAR_NUM_7 + 1; j++) // base 8
      ch_found = ch_found || (cur_ch == (str -> num)[j]);
    if (ch_found == owl_false)
      return i;
    i += str -> check_wchar_enc(ptr + i, size - i);
  }
  
  // end of string reached
  return size;
}

// get integer inside a string as an octal number
owl_umax owl_get_str_as_oct(owl_str * str, owl_byte * ptr)
{
  // check params
  owl_umax size = owl_check_str_as_oct(str, ptr);
  if (size == 0)
    return 0;

  // get the character count for the number
  owl_str tmp = *str;
  owl_mem_space sp = {ptr, size};
  tmp.arr = &sp;
  owl_umax ch_count = owl_get_str_ch_length(&tmp) - 1,  
           i = str -> check_wchar_enc(ptr, str -> arr -> ptr + str -> arr -> size - ptr),
           rtn = 0, pow8 = 1;
  // fast power of 8
  for (owl_umax j = 1; j < ch_count; j++)
    pow8 = pow8 << 3;
  // calculate the actual number
  for (/**/; i < size; /**/) {
    owl_wchar cur_ch  = str -> get_wchar(ptr + i, size - i);
    rtn += pow8 * owl_get_num_ch_as_num(str, cur_ch);
    pow8 /= 8;
    i += str -> check_wchar(cur_ch);
  }
  // return the number
  return rtn;
}

// check integer inside a string as a decimal number
owl_umax owl_check_str_as_dec(owl_str * str, owl_byte * ptr)
{
  // check params
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false
      || ptr < (str -> arr -> ptr)
      || ptr > (str -> arr -> ptr) + (str -> arr -> size) - 1
      || owl_check_str_pos(str, ptr - str -> arr -> ptr) == owl_false)
    return 0;
  
  // first character must not be 0 (it might be 0 if it is just the number 0)
  // ptr must point to the start of a character's data
  owl_umax size = (str -> arr -> ptr) + (str -> arr -> size) - ptr,
           i = str -> check_wchar_enc(ptr, size);
  owl_bool zero_first = owl_false;
  if (str -> get_wchar(ptr, size) == (str -> num)[OWL_WCHAR_NUM_0]) {
    // check if the next character is numerical
    if (size - i == 0) // it is only the character 0
      return i;
    zero_first = owl_true;
  }
  // check each character to see if it is from an octal integer
  // return the size in bytes of the number read
  for (i = 0; i < size; /**/) {
    owl_wchar cur_ch = str -> get_wchar(ptr + i, size - i);
    owl_bool ch_found = owl_false;
    for (owl_umax j = OWL_WCHAR_NUM_0; j < OWL_WCHAR_NUM_9 + 1; j++) // base 10
      ch_found = ch_found || (cur_ch == (str -> num)[j]);
    if (ch_found == owl_false || (cur_ch != (str -> num)[OWL_WCHAR_NUM_0] && zero_first))
      return i;
    i += str -> check_wchar_enc(ptr + i, size - i);
  }
  
  // end of string reached
  return size;
}

// get integer inside a string as a decimal number
owl_umax owl_get_str_as_dec(owl_str * str, owl_byte * ptr)
{
  // check params
  owl_umax size = owl_check_str_as_dec(str, ptr);
  if (size == 0)
    return 0;

  // get the character count for the number
  owl_str tmp = *str;
  owl_mem_space sp = {ptr, size};
  tmp.arr = &sp;
  owl_umax ch_count = owl_get_str_ch_length(&tmp),  
           rtn = 0, pow10 = 1;
  // fast power of 10 (I think)
  for (owl_umax i = 1; i < ch_count; i++)
    pow10 = (pow10 << 3) + (pow10 << 1);
  // calculate the actual number
  for (owl_umax i = 0; i < size; /**/) {
    owl_wchar cur_ch = str -> get_wchar(ptr + i, size - i);
    rtn += pow10 * owl_get_num_ch_as_num(str, cur_ch);
    pow10 /= 10;
    i += str -> check_wchar(cur_ch);
  }
  
  // return the number
  return rtn;
}

// check integer inside a string as a hexadecimal number
owl_umax owl_check_str_as_hex(owl_str * str, owl_byte * ptr)
{
  // check params
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false
      || ptr < (str -> arr -> ptr)
      || ptr > (str -> arr -> ptr) + (str -> arr -> size) - 1
      || owl_check_str_pos(str, ptr - str -> arr -> ptr) == owl_false)
    return 0;
  
  // first character must be 0 and second character must be x or X
  // ptr must point to the start of a character's data  
  // zero check
  owl_umax i = 0, size = (str -> arr -> ptr) + (str -> arr -> size) - ptr;
  owl_byte ch_size = str -> check_wchar_enc(ptr + i, size - i);
  if (ch_size == 0
      || str -> get_wchar(ptr + i, size - i) != (str -> num)[OWL_WCHAR_NUM_0])
    return 0;
  i += ch_size;
  // x/X check
  ch_size = str -> check_wchar_enc(ptr + i, size - i);
  if (ch_size == 0
      || (str -> get_wchar(ptr + i, size - i) != (str -> num)[OWL_WCHAR_NUM_X_UP]
          && str -> get_wchar(ptr + i, size - i) != (str -> num)[OWL_WCHAR_NUM_X_LOW]))
    return 0;
  i += ch_size;
  
  // check each character to see if it is from an octal integer
  // return the size in bytes of the number read
  owl_umax tmp = i;
  for (/**/; i < size; /**/) {
    owl_wchar cur_ch = str -> get_wchar(ptr + i, size - i);
    owl_bool ch_found = owl_false;
    for (owl_umax j = OWL_WCHAR_NUM_0; j < OWL_WCHAR_NUM_F_LOW + 1; j++) // base 16
      ch_found = ch_found || (cur_ch == (str -> num)[j]);
    // first character after the x/X must be hex
    if (tmp == i && ch_found == owl_false)
      return 0;
    if (ch_found == owl_false)
      return i;
    i += str -> check_wchar_enc(ptr + i, size - i);
  }
  
  // end of string reached
  return size;
}

// get integer inside a string as a hexadecimal number
owl_umax owl_get_str_as_hex(owl_str * str, owl_byte * ptr)
{
  // check params
  owl_umax size = owl_check_str_as_hex(str, ptr);
  if (size == 0)
    return 0;

  // get the character count for the number
  owl_str tmp = *str;
  owl_mem_space sp = {ptr, size};
  tmp.arr = &sp;
  owl_umax ch_count = owl_get_str_ch_length(&tmp),
           rtn = 0, pow16 = 1;
  // fast power of 16
  for (owl_umax i = 1; i < ch_count; i++)
    pow16 = (pow16 << 4);
  // calculate the actual number
  for (owl_umax i = 0; i < size; /**/) {
    owl_wchar cur_ch  = str -> get_wchar(ptr + i, size - i);
    rtn += pow16 * owl_get_num_ch_as_num(str, cur_ch);
    pow16 /= 16;
    i += str -> check_wchar(cur_ch);
  }
  // return the number
  return rtn;
}

// check if a character is a sign (plus/minus)
owl_umax owl_check_sign_mult(owl_str * str, owl_byte * ptr)
{
  // check params
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false
      || ptr < (str -> arr -> ptr)
      || ptr > (str -> arr -> ptr) + (str -> arr -> size) - 1
      || owl_check_str_pos(str, ptr - str -> arr -> ptr) == owl_false)
    goto err;
  
  // check if it is a valid character position
  owl_wchar ch = str -> get_wchar(ptr, (str -> arr -> ptr) + (str -> arr -> size) - ptr);
  if (ch == str -> inv)
    goto err;
  
  // check if the character is plus/minus
  if (ch == (str -> num)[OWL_WCHAR_NUM_PLUS] || ch == (str -> num)[OWL_WCHAR_NUM_MINUS])
    return str -> check_wchar(ch);
  
  // error
  err:
  return 0;
}

// get a sign character as a multiplier
owl_char owl_get_sign_mult(owl_str * str, owl_byte * ptr)
{
  // check params
  owl_byte ch_size = owl_check_sign_mult(str, ptr);
  if (ch_size == 0)
    return 0;
    
  // get the character
  owl_wchar ch = str -> get_wchar(ptr, ch_size);
  owl_char sign = 1;
  if (ch == (str -> num)[OWL_WCHAR_NUM_MINUS])
    sign = -1;
  
  // return the sign
  return sign;
}

// check a str as an unsigned integer
owl_umax owl_check_str_as_uint(owl_str * str, owl_byte * ptr)
{
  // check base (decimal > hexadecimal > octal)
  owl_umax size = 0;
  if ((size = owl_check_str_as_dec(str, ptr)) != 0)
    return size;
  else if ((size = owl_check_str_as_hex(str, ptr)) != 0)
    return size;
  return owl_check_str_as_oct(str, ptr);
}

// get a str as an unsigned integer
owl_umax owl_get_str_as_uint(owl_str * str, owl_byte * ptr)
{
  // check params
  owl_umax size = owl_check_str_as_uint(str, ptr);
  if (size == 0)
    return 0;
  
  // get the number
  owl_umax rtn = owl_get_str_as_dec(str, ptr);
  if (rtn != 0)
    return rtn;
  else if ((rtn = owl_get_str_as_hex(str, ptr)) != 0)
    return rtn;
  return owl_get_str_as_oct(str, ptr);
}

// check a str as a signed integer
owl_umax owl_check_str_as_sint(owl_str * str, owl_byte * ptr)
{
  // check sign
  owl_char sign = owl_get_sign_mult(str, ptr);
  // add the ch size to ptr if there is a sign
  owl_umax sign_size = 0;
  if (sign == 1)
    sign_size = str -> check_wchar((str -> num)[OWL_WCHAR_NUM_PLUS]);
  else if (sign == -1)
    sign_size = str -> check_wchar((str -> num)[OWL_WCHAR_NUM_MINUS]);
  
  // check number
  owl_umax num_size = owl_check_str_as_uint(str, ptr + sign_size);
  return num_size == 0 ? 0 : num_size + sign_size;
}

// get str as a signed integer
owl_smax owl_get_str_as_sint(owl_str * str, owl_byte * ptr)
{
  // check params
  owl_umax size = owl_check_str_as_sint(str, ptr);
  if (size == 0)
    return 0;
  // get the sign
  owl_char sign = owl_get_sign_mult(str, ptr);
  owl_umax sign_size = 0;
  if (sign != 0)
    sign_size = str -> check_wchar_enc(ptr, size);
  // get the number
  return (sign == 0 ? 1 : sign) * owl_get_str_as_uint(str, ptr + sign_size);
}

// check str as a float number
owl_umax owl_check_str_as_float(owl_str * str, owl_byte * ptr)
{
  // only decimal representation
  // examples:
  //   -123e10
  //   9.8E10
  //   123.898989
  //   1.23E+123
  //   0.5e-2
  //   0.1
  //   123
  //   pos = possible
  //   [pos sign][number][pos dot][pos number][pos e/E][pos sign][number]
  
  // check params
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false
      || ptr < (str -> arr -> ptr)
      || ptr > (str -> arr -> ptr) + (str -> arr -> size) - 1)
    return 0;
  
  // check for unsigned/signed number
  owl_umax size_rtn = owl_check_sign_mult(str, ptr), // sign
           read_size = (str -> arr -> ptr) + (str -> arr -> size) - ptr,
           tmp = 0;
  if ((tmp = owl_check_str_as_dec(str, ptr + size_rtn)) == 0) // number
      return 0;
  size_rtn += tmp;
  
  // check possible dot
  if (str -> get_wchar(ptr + size_rtn, read_size - size_rtn) == (str -> num)[OWL_WCHAR_NUM_DOT]) {
    size_rtn += str -> check_wchar_enc(ptr + size_rtn, read_size - size_rtn);
    // check number (has to exist, unsigned integer, it will be in decimal but it can have 0s at the start)
    // check for zeroes
    tmp = size_rtn;
    while (str -> get_wchar(ptr + size_rtn, read_size - size_rtn) == (str -> num)[OWL_WCHAR_NUM_0])
      size_rtn += str -> check_wchar((str -> num)[OWL_WCHAR_NUM_0]);
    // decimal check
    if (tmp == size_rtn && owl_check_str_as_dec(str, ptr + size_rtn) == 0)
      return 0;
    size_rtn += owl_check_str_as_dec(str, ptr + size_rtn);  
  }
  
  // check possible e/E
  owl_wchar exp_ch = str -> get_wchar(ptr + size_rtn, read_size - size_rtn);
  if (exp_ch == (str -> num)[OWL_WCHAR_NUM_E_UP] || exp_ch == (str -> num)[OWL_WCHAR_NUM_E_LOW]) {
    size_rtn += str -> check_wchar_enc(ptr + size_rtn, read_size - size_rtn);
    // check number (has to exist, unsigned/signed integer)
    size_rtn += owl_check_sign_mult(str, ptr + size_rtn); // sign
    if ((tmp = owl_check_str_as_dec(str, ptr + size_rtn)) == 0) // number
      return 0;
    size_rtn += tmp;
  }
  
  // return the number of bytes read
  return size_rtn;
}

// get str as a float number
owl_fmax owl_get_str_as_float(owl_str * str, owl_byte * ptr)
{
  // check params
  owl_umax size = owl_check_str_as_float(str, ptr);
  if (size == 0)
    return 0;
  
  // return the number
  owl_fmax num = 0;
  
  // get signed/unsigned number
  owl_char sign = owl_get_sign_mult(str, ptr);
  owl_umax pos = 0;
  if (sign == 0)
    sign = 1;
  else
    pos += str -> check_wchar_enc(ptr, size);
  num = sign * ((owl_fmax) owl_get_str_as_dec(str, ptr + pos));
  pos += owl_check_str_as_dec(str, ptr + pos);
  
  // get possible dot number
  if (str -> get_wchar(ptr + pos, size - pos) == (str -> num)[OWL_WCHAR_NUM_DOT]) {
    pos += str -> check_wchar((str -> num)[OWL_WCHAR_NUM_DOT]);
    // get the power of 10 number
    owl_wchar cur_ch = str -> get_wchar(ptr + pos, size - pos);
    owl_fmax pow10 = 0.1f; // to be used inside the while loop
    while (cur_ch == (str -> num)[OWL_WCHAR_NUM_0]
           || cur_ch == (str -> num)[OWL_WCHAR_NUM_1]
           || cur_ch == (str -> num)[OWL_WCHAR_NUM_2]
           || cur_ch == (str -> num)[OWL_WCHAR_NUM_3]
           || cur_ch == (str -> num)[OWL_WCHAR_NUM_4]
           || cur_ch == (str -> num)[OWL_WCHAR_NUM_5]
           || cur_ch == (str -> num)[OWL_WCHAR_NUM_6]
           || cur_ch == (str -> num)[OWL_WCHAR_NUM_7]
           || cur_ch == (str -> num)[OWL_WCHAR_NUM_8]
           || cur_ch == (str -> num)[OWL_WCHAR_NUM_9]) {
      num += (sign == 1 ? 1 : -1) * pow10 * owl_get_num_ch_as_num(str, cur_ch);
      pow10 /= 10;
      pos += str -> check_wchar(cur_ch);
      cur_ch = str -> get_wchar(ptr + pos, size - pos);
    }
  }  
  
  // get possible exponent
  owl_wchar exp_ch = str -> get_wchar(ptr + pos, size - pos);
  if (exp_ch == (str -> num)[OWL_WCHAR_NUM_E_UP] || exp_ch == (str -> num)[OWL_WCHAR_NUM_E_LOW]) {
    pos += str -> check_wchar_enc(ptr + pos, size - pos);
    // get sign
    sign = owl_get_sign_mult(str, ptr + pos);
    if (sign == 0) sign = 1;
    else pos += str -> check_wchar_enc(ptr + pos, size - pos);
    // get number
    owl_smax exp = sign * (owl_smax) owl_get_str_as_dec(str, ptr + pos);
    //~ printf("exp %lld num %lf\n", exp, num);
    if (exp > 0) while (exp-- != 0) num *= 10;
    else if (exp < 0) while (exp++ != 0) num /= 10;
  }
  
  // done!
  return num;
}

// write number as str (in octal)
owl_byte * owl_write_num_as_str_oct(owl_umax num, owl_str * str, owl_byte * ptr)
{
  // check params
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false
      || ptr < (str -> arr -> ptr)
      || ptr > (str -> arr -> ptr) + (str -> arr -> size) - 1
      || owl_check_str_pos(str, ptr - str -> arr -> ptr) == owl_false)
    return NULL;
  
  // calculate number encoded size to check if it is able to be written
  owl_umax num_enc_size = 0, tmp = num;
  do {
    num_enc_size += str -> check_wchar((str -> num)[owl_get_num_as_num_ch_pos((owl_byte) (tmp & 7))]);
    tmp = tmp >> 3; // same as (tmp / 8)
  } while (tmp != 0);
  // add the size of the zero to add at the start!
  num_enc_size += str -> check_wchar((str -> num)[OWL_WCHAR_NUM_0]);
  
  // check if the write process is valid
  if (num_enc_size > (str -> arr -> ptr) + (str -> arr -> size) - ptr)
    return NULL;
  
  // write the number (in reverse, then I reuse owl_reverse_str)
  tmp = num;
  for (owl_umax i = 0; i < num_enc_size; /**/) {
    owl_wchar num_ch = *owl_get_num_as_num_ch(str, (owl_byte) (tmp & 7));
    str -> write_wchar_enc(num_ch, ptr + i, num_enc_size - i);
    i += str -> check_wchar(num_ch);
    tmp = (tmp >> 3);
  }
  
  // reverse the array!
  owl_str tmp_str = *str;
  owl_mem_space tmp_sp = {ptr, num_enc_size};
  tmp_str.arr = &tmp_sp;
  owl_reverse_str(&tmp_str);
  
  // done!
  return ptr + num_enc_size;
}

// write number as str (in decimal)
owl_byte * owl_write_num_as_str_dec(owl_umax num, owl_str * str, owl_byte * ptr)
{
  // check params
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false
      || ptr < (str -> arr -> ptr)
      || ptr > (str -> arr -> ptr) + (str -> arr -> size) - 1
      || owl_check_str_pos(str, ptr - str -> arr -> ptr) == owl_false)
    return NULL;
  
  // calculate number encoded size to check if it is able to be written
  owl_umax num_enc_size = 0, tmp = num;
  do {
    owl_wchar num_ch = *owl_get_num_as_num_ch(str, (owl_byte) (tmp - (((owl_umax) (tmp / 10)) * 10)));
    num_enc_size += str -> check_wchar(num_ch);
    tmp = tmp / 10;
  } while (tmp != 0);
  
  // check if the write process is valid
  if (num_enc_size > (str -> arr -> ptr) + (str -> arr -> size) - ptr)
    return NULL;
  
  // write the number (in reverse, then I reuse owl_reverse_str)
  tmp = num;
  for (owl_umax i = 0; i < num_enc_size; /**/) {
    owl_wchar num_ch = *owl_get_num_as_num_ch(str, (owl_byte) (tmp - (((owl_umax) (tmp / 10)) * 10)));
    str -> write_wchar_enc(num_ch, ptr + i, num_enc_size - i);
    i += str -> check_wchar(num_ch);
    tmp = tmp / 10;
  }
  
  // reverse the array!
  owl_str tmp_str = *str;
  owl_mem_space tmp_sp = {ptr, num_enc_size};
  tmp_str.arr = &tmp_sp;
  owl_reverse_str(&tmp_str);
  
  // done!
  return ptr + num_enc_size;
}

// write number as str (in hexadecimal)
owl_byte * owl_write_num_as_str_hex(owl_umax num, owl_str * str, owl_byte * ptr)
{
  // check params
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false
      || ptr < (str -> arr -> ptr)
      || ptr > (str -> arr -> ptr) + (str -> arr -> size) - 1
      || owl_check_str_pos(str, ptr - str -> arr -> ptr) == owl_false)
    return NULL;
  
  // calculate number encoded size to check if it is able to be written
  owl_umax num_enc_size = 0, tmp = num;
  do {
    owl_wchar num_ch = *owl_get_num_as_num_ch(str, (owl_byte) (tmp & 15));
    num_enc_size += str -> check_wchar(num_ch);
    tmp = tmp >> 4; // same as (tmp / 16)
  } while (tmp != 0);
  // add the size of the zero and x to add at the start!
  owl_byte zero_x_size = str -> check_wchar((str -> num)[OWL_WCHAR_NUM_0])
                         + str -> check_wchar((str -> num)[OWL_WCHAR_NUM_X_LOW]);
  num_enc_size += zero_x_size;
  
  // check if the write process is valid
  if (num_enc_size > (str -> arr -> ptr) + (str -> arr -> size) - ptr)
    return NULL;
  
  // write the zero and the x already
  str -> write_wchar_enc((str -> num)[OWL_WCHAR_NUM_0], ptr, num_enc_size);
  owl_umax i = str -> check_wchar((str -> num)[OWL_WCHAR_NUM_0]);
  str -> write_wchar_enc((str -> num)[OWL_WCHAR_NUM_X_LOW], ptr + i, num_enc_size - i);
  i += str -> check_wchar((str -> num)[OWL_WCHAR_NUM_X_LOW]);
  
  // write the number (in reverse, then I reuse owl_reverse_str)
  tmp = num;
  for (/**/; i < num_enc_size; /**/) {
    owl_wchar num_ch = *owl_get_num_as_num_ch(str, (owl_byte) (tmp & 15));
    str -> write_wchar_enc(num_ch, ptr + i, num_enc_size - i);
    i += str -> check_wchar(num_ch);
    tmp = (tmp >> 4);
  }
  
  // reverse the array!
  owl_str tmp_str = *str;
  owl_mem_space tmp_sp = {ptr + zero_x_size, num_enc_size - zero_x_size};
  tmp_str.arr = &tmp_sp;
  owl_reverse_str(&tmp_str);
  
  // done!
  return ptr + num_enc_size;
}

// write number as str (decimal, just a reuse of the above function)
owl_byte * owl_write_uint_as_str(owl_umax num, owl_str * str, owl_byte * ptr)
{
  return owl_write_num_as_str_dec(num, str, ptr);
}

// write signed number as str (decimal)
owl_byte * owl_write_sint_as_str(owl_smax num, owl_str * str, owl_byte * ptr)
{
  // check params
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false
      || ptr < (str -> arr -> ptr)
      || ptr > (str -> arr -> ptr) + (str -> arr -> size) - 1
      || owl_check_str_pos(str, ptr - str -> arr -> ptr) == owl_false)
    return NULL;
  
  // check if it is negative
  owl_umax write_pos = 0;
  if (num < 0) {
    write_pos = str -> check_wchar((str -> num)[OWL_WCHAR_NUM_MINUS]);
    num *= -1;
  }
  
  // cast to unsigned integer and write that
  owl_byte * tmp = owl_write_uint_as_str(num, str, ptr + write_pos);
  if (ptr == NULL)
    return NULL;
  
  // write the sign if negative
  if (write_pos != 0)
    str -> write_wchar_enc((str -> num)[OWL_WCHAR_NUM_MINUS], ptr, write_pos);
  return tmp;
}

// write float as str (decimal)
owl_byte * owl_write_float_as_str(owl_fmax num, owl_str * str, owl_byte * ptr)
{  
  // check params
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false
      || ptr < (str -> arr -> ptr)
      || ptr > (str -> arr -> ptr) + (str -> arr -> size) - 1
      || owl_check_str_pos(str, ptr - str -> arr -> ptr) == owl_false)
    return NULL;
  
  // define this function
  owl_byte floor_from_0_to_9(owl_fmax num)
  {
    if (num >= 9.0f) return 9;
    else if (num >= 8.0f) return 8;
    else if (num >= 7.0f) return 7;
    else if (num >= 6.0f) return 6;
    else if (num >= 5.0f) return 5;
    else if (num >= 4.0f) return 4;
    else if (num >= 3.0f) return 3;
    else if (num >= 2.0f) return 2;
    else if (num >= 1.0f) return 1;
    return 0;
  }
  
  // I have to do some numerical considerations here (feck)
  // if the number is too low or too high it will be written in exponential form
  // otherwise it will be written in normal form (with a dot)
  owl_byte buffer[30] = {0};
  owl_byte bcount = 0;  
  
  // will just use 10^5 or 10^-5 as the range for dot written
  // anything outside that is going to be exponential written
  owl_umax enc_num_size = 0;  
  
  // calculate number encoded size to check if it is able to be written in str
  owl_fmax tmp1 = num, tmp2 = num;    
  owl_umax pow10 = 1;
  owl_smax exp10 = 0;
    
  ///////
  // sign
  if (tmp1 < 0.0f) {
    enc_num_size += str -> check_wchar((str -> num)[OWL_WCHAR_NUM_MINUS]);
    buffer[bcount++] = OWL_WCHAR_NUM_MINUS;
    tmp1 *= -1;
    tmp2 = tmp1;
  }
  
  // define the ranges
  if (tmp1 <= 1000000.0f && tmp1 >= 0.00001f)
  {    
    ///////////////
    // integer part
    // get maximum power of 10 that has the 
    // same digit count as the number (integer part)
    while (tmp1 >= 10.0f) {tmp1 /= 10.0f; exp10++;}
    // fast power of 10
    for (owl_umax i = 0; i < exp10; i++)
      pow10 = (pow10 << 3) + (pow10 << 1);
    // substract most significative power of 10
    tmp1 = tmp2 / pow10;
    while (pow10 >= 1.0f) {
      // selection of the most significative number
      owl_byte tmp_floor = floor_from_0_to_9(tmp1);
      buffer[bcount] = owl_get_num_as_num_ch_pos(tmp_floor);
      // substraction
      tmp2 -= (tmp_floor * pow10);
      pow10 /= 10.0f;
      tmp1 = tmp2 / pow10;
      enc_num_size += str -> check_wchar((str -> num)[buffer[bcount++]]);
    }
    
    //////
    // dot
    enc_num_size += str -> check_wchar((str -> num)[OWL_WCHAR_NUM_DOT]);
    buffer[bcount++] = OWL_WCHAR_NUM_DOT;
    
    //////////////////
    // fractional part
    // store the 7 first digits
    tmp2 *= 10.0f;
    for (owl_byte i = 0; i < 7; i++) {
      owl_byte tmp_floor = floor_from_0_to_9(tmp2);
      buffer[bcount] = owl_get_num_as_num_ch_pos(tmp_floor);
      tmp2 -= tmp_floor;
      tmp2 *= 10;
      enc_num_size += str -> check_wchar((str -> num)[buffer[bcount++]]);
    }
  } 
  else // exponential form
  {
    // considerations if the number is too big or if it is too small
    if (tmp1 > 1000000.0f)
      while (tmp1 >= 10.0f) {exp10++; tmp1 /= 10;}
    else
      while (tmp1 <= 1.0f) {exp10--; tmp1 *= 10;}
    
    ///////////////
    // integer part
    // only 1 digit
    // selection of the most significative number
    owl_byte tmp_floor = floor_from_0_to_9(tmp1);
    buffer[bcount] = owl_get_num_as_num_ch_pos(tmp_floor);
    tmp1 -= tmp_floor;
    enc_num_size += str -> check_wchar((str -> num)[buffer[bcount++]]);
    
    //////
    // dot
    buffer[bcount++] = OWL_WCHAR_NUM_DOT;
    enc_num_size += str -> check_wchar((str -> num)[OWL_WCHAR_NUM_DOT]);
    
    //////////////////
    // fractional part
    // store the 7 first digits
    tmp1 *= 10.0f;
    for (owl_byte i = 0; i < 7; i++) {
      tmp_floor = floor_from_0_to_9(tmp1);
      buffer[bcount] = owl_get_num_as_num_ch_pos(tmp_floor);
      tmp1 -= tmp_floor;
      tmp1 *= 10;
      enc_num_size += str -> check_wchar((str -> num)[buffer[bcount++]]);
    }
    
    /////////////////////////////////
    // exponent symbol and its number
    enc_num_size += str -> check_wchar((str -> num)[OWL_WCHAR_NUM_E_LOW]); // lowercase e
    buffer[bcount++] = OWL_WCHAR_NUM_E_LOW;
    if (exp10 < 0) {
      enc_num_size += str -> check_wchar((str -> num)[OWL_WCHAR_NUM_MINUS]); // minus symbol
      buffer[bcount++] = OWL_WCHAR_NUM_MINUS;
      exp10 *= -1;
    }
    // actual exponent number (will be written backwards in buffer)
    owl_byte exp_ch_size = 0;
    do {
      buffer[bcount++] = owl_get_num_as_num_ch_pos(exp10 - (((owl_smax) (exp10 / 10)) * 10));
      exp10 /= 10;
      exp_ch_size++;
    } while (exp10 != 0);
    
    // reverse the buffer section with the exponent part
    owl_reverse_bytarr(&((owl_bytarr) {buffer + bcount - exp_ch_size, exp_ch_size}));      
  }
  
  // buffer end o7 (just a trick)
  buffer[bcount] = OWL_WCHAR_NUM_A_UP;
  
  // check if the write process is valid
  if (enc_num_size > (str -> arr -> ptr) + (str -> arr -> size) - ptr)
    return NULL;    
  // write the number
  for (owl_byte i = 0, * tmp_ptr = ptr; buffer[i] != OWL_WCHAR_NUM_A_UP; i++)
    tmp_ptr = str -> write_wchar_enc((str -> num)[buffer[i]], tmp_ptr, str -> arr -> size);
  
  // done!
  return ptr + enc_num_size;
}

// hash function that does the following math
// note: bytes are interpreted as signed 8 bit integers
// hash = [FIRST_BYTE_VALUE] * (3^(STR_BYTE_LENGTH - 1)) 
//        + [SECOND_BYTE_VALUE] * (3^(STR_BYTE_LENGTH - 2))
//        + [THIRD_BYTE_VALUE] * (3^(STR_BYTE_LENGTH - 3))
//        + ...
//        + [LAST_BYTE_VALUE] * (3^(0))
// result is truncated to 16 bits
// used in some JSYSTEM (Super Mario Galaxy) files
owl_umax owl_calc_str_schar_pol3_hash(owl_str * str)
{
  // check params
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false)
    return 0;
  
  // calculate the hash
  owl_umax result = 0, pow3 = 1;
  for (owl_umax i = 0; i < str -> arr -> size; i++) {
    result += pow3 * (owl_char) (str -> arr -> ptr)[str -> arr -> size - i - 1];
    pow3 = pow3 + (pow3 << 1); // fast power of 3?
  }
  
  // done!
  return result;
}

// hash function that does the following math
// note: bytes are interpreted as signed 8 bit integers
// hash = [FIRST_BYTE_VALUE] * (31^(STR_BYTE_LENGTH - 1)) 
//        + [SECOND_BYTE_VALUE] * (31^(STR_BYTE_LENGTH - 2))
//        + [THIRD_BYTE_VALUE] * (31^(STR_BYTE_LENGTH - 3))
//        + ...
//        + [LAST_BYTE_VALUE] * (31^(0))
// result is truncated to 32 bits
// used in some JSYSTEM (Super Mario Galaxy) files
owl_umax owl_calc_str_schar_pol31_hash(owl_str * str)
{
  // check params
  if (owl_check_str_all(str, sizeof(owl_str)) == owl_false)
    return 0;
  
  // calculate the hash
  owl_umax result = 0, pow31 = 1;
  for (owl_umax i = 0; i < str -> arr -> size; i++) {
    result += pow31 * (owl_char) (str -> arr -> ptr)[str -> arr -> size - i - 1];
    pow31 = (pow31 << 5) - pow31; // fast power of 31?
  }
  
  // done!
  return result;
}
